package com.ssdp.utils;

import cn.hutool.core.lang.UUID;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * @author syo 2319615164@qq.com
 * @description 分布式锁简单实现
 * @date 2024/3/14
 * @Copyright syo
 */
@SuppressWarnings("all")
public class SimpleRedisLock implements ILock{

    // 不同业务锁的名称不一样， 不同的业务有不同的锁
    private String name;

    // redis锁的统一前缀，显得专业一点
    private static final String KEY_PREFIX = "lock:";

    // 值前缀
    private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";

    // redis客户端
    private StringRedisTemplate stringRedisTemplate;

    // 脚本类型实现类，泛型为返回值类型
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;

    // 静态代码块，加载脚本
    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    /**
     * 构造方法
     * @param name                  业务名
     * @param stringRedisTemplate   客户端
     */
    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }


    /**
     * 获取锁
     * @param timeoutSec    超时释放时间
     * @return              结果
     */
    @Override
    public boolean tryLock(long timeoutSec) {

        // 获取线程的标识
        long threadId = Thread.currentThread().getId();

        // value
        String threadIdStr = ID_PREFIX + threadId;

        // key
        String key = KEY_PREFIX + name;

        // key为前缀加业务， value就是线程标识
        // 获取锁时同时设置过期时间，保证命令原子性
        // 非阻塞锁
        Boolean isSuccess = stringRedisTemplate.opsForValue().setIfAbsent(key, threadIdStr, timeoutSec, TimeUnit.SECONDS);

        // 谨防空指针
        return Boolean.TRUE.equals(isSuccess);
    }

    /**
     * 释放锁
     */
//    @Override
//    public void unlock() {
//        // 获取线程标识
//        long threadId = Thread.currentThread().getId();
//        String threadIdStr = ID_PREFIX + threadId;
//
//        // 获取锁中的标识
//        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
//
//        if(threadIdStr.equals(id)) {
//            stringRedisTemplate.delete(KEY_PREFIX + name);
//        }
//    }


//    @Override
//    public void unlock() {
//        stringRedisTemplate.delete(KEY_PREFIX + name);
//    }


    @Override
    public void unlock() {
        stringRedisTemplate.execute(UNLOCK_SCRIPT,
                Collections.singletonList(KEY_PREFIX + name), ID_PREFIX + Thread.currentThread().getId());
    }
}
